#ifndef NANE_MEMBERFUNCTIONPTR_H
#   define NANE_MEMBERFUNCTIONPTR_H

#   include "nane/core/function/FunctionBase.h"

namespace nane
{
    template<class C, typename RT
                    , typename P1 = void
                    , typename P2 = void
                    , typename P3 = void
                    , typename P4 = void>
    struct MemberFunctionPtrT
    {
        typedef RT (C::*Type)(P1,P2,P3,P4);
    };

    template<class C, typename RT, typename P1, typename P2, typename P3>
    struct MemberFunctionPtrT<C, RT, P1, P2, P3>
    {
        typedef RT (C::*Type)(P1,P2,P3);
    };

    // partial specialization for two parameters:
    template<class C, typename RT, typename P1, typename P2>
    struct MemberFunctionPtrT<C, RT, P1, P2, void>
    {
        typedef RT (C::*Type)(P1,P2);
    };

    // partial specialization for one parameter:
    template<class C, typename RT, typename P1>
    struct MemberFunctionPtrT<C, RT, P1, void, void>
    {
        typedef RT (C::*Type)(P1);
    };

    // partial specialization for no parameters:
    template<class C, typename RT>
    struct MemberFunctionPtrT<C, RT, void, void, void>
    {
        typedef RT (C::*Type)();
    };

    template<class C, typename RT
                    , typename P1 = void
                    , typename P2 = void
                    , typename P3 = void
                    , typename P4 = void>
    class MemberFunctionPtr
    {
    public:
        typedef typename MemberFunctionPtrT<C,RT,P1,P2,P3,P4>::Type TFuncPtr;
        typedef RT TReturn;
        typedef P1 TParam1;
        typedef P2 TParam2;
        typedef P3 TParam3;
        typedef P4 TParam4;

    private:
        // the encapsulated pointer:
        TFuncPtr m_function;
        C* m_object;

    public:
        MemberFunctionPtr( TFuncPtr _ptr, C* _object )
            : m_function( _ptr )
            , m_object( _object )
        {
        }

        TReturn Call()
        {
            return (m_object->*m_function)();
        }

        TReturn Call( typename ForwardParamT<TParam1>::Type _a1 )
        {
            return (m_object->*m_function)(_a1);
        }

        TReturn Call( typename ForwardParamT<TParam1>::Type _a1
                    , typename ForwardParamT<TParam2>::Type _a2 )
        {
            return (m_object->*m_function)(_a1, _a2);
        }

        TReturn Call( typename ForwardParamT<TParam1>::Type _a1
                    , typename ForwardParamT<TParam2>::Type _a2
                    , typename ForwardParamT<TParam3>::Type _a3 )
        {
            return (m_object->*m_function)(_a1, _a2, _a3);
        }

        TReturn Call( typename ForwardParamT<TParam1>::Type _a1
                    , typename ForwardParamT<TParam2>::Type _a2
                    , typename ForwardParamT<TParam3>::Type _a3
                    , typename ForwardParamT<TParam4>::Type _a4 )
        {
            return (m_object->*m_function)(_a1, _a2, _a3, _a4);
        }
    };

    template<class C, typename RT> inline
    Functor< MemberFunctionPtr<C,RT> > MakeMemberFunctor( RT (C::*mfp)(), C* _object )
    {
        return Functor< MemberFunctionPtr<C,RT> >(MemberFunctionPtr<C,RT>(mfp, _object));
    }

    template<class C, typename RT, typename P1> inline
    Functor< MemberFunctionPtr<C, RT, P1> > MakeMemberFunctor( RT (C::*mfp)(P1), C* _object )
    {
        return Functor< MemberFunctionPtr<C, RT, P1> >(MemberFunctionPtr<C, RT, P1>(mfp, _object));
    }

    template<class C, typename RT, typename P1, typename P2> inline
    Functor< MemberFunctionPtr<C, RT, P1, P2> > MakeMemberFunctor( RT (C::*mfp)(P1,P2), C* _object )
    {
        return Functor< MemberFunctionPtr<C, RT, P1, P2> >(MemberFunctionPtr<C, RT, P1, P2>(mfp, _object));
    }

    template<class C, typename RT, typename P1, typename P2, typename P3> inline
    Functor< MemberFunctionPtr<C, RT, P1, P2, P3> > MakeMemberFunctor( RT (C::*mfp)(P1,P2,P3), C* _object )
    {
        return Functor< MemberFunctionPtr<C, RT, P1, P2, P3> >(MemberFunctionPtr<C, RT, P1, P2, P3>(mfp, _object));
    }

    template<class C, typename RT, typename P1, typename P2, typename P3, typename P4> inline
    Functor< MemberFunctionPtr<C, RT, P1, P2, P3, P4> > MakeMemberFunctor( RT (C::*mfp)(P1,P2,P3,P4), C* _object )
    {
        return Functor< MemberFunctionPtr<C, RT, P1, P2, P3, P4> >(MemberFunctionPtr<C, RT, P1, P2, P3, P4>(mfp, _object));
    }
}   // namespace nane

#endif	// NANE_MEMBERFUNCTIONPTR_H

